package heap

import (
	"lesson01/classfile"
	"strings"
)

type Class struct {
	accessFlags       uint16   //访问标志
	name              string   //类名
	superClassName    string   //超类名
	interfaceNames    []string //接口名
	constantPool      *ConstantPool
	fields            []*Field
	methods           []*Method
	loader            *ClassLoader
	superClass        *Class
	interfaces        []*Class
	instanceSlotCount uint //实例变量
	staticSlotCount   uint //类变量
	staticVars        Slots
	initStarted       bool
	jClass            *Object
}

func newClass(cf *classfile.ClassFile) *Class {
	clazz := &Class{}
	clazz.accessFlags = cf.AccessFlags()
	clazz.name = cf.ClassName()
	clazz.superClassName = cf.SuperClassName()
	clazz.interfaceNames = cf.InterfaceNames()
	//常量池
	clazz.constantPool = newConstantPool(clazz, cf.ConstantPool())
	//属性
	clazz.fields = newField(clazz, cf.Fields())
	//方法
	clazz.methods = newMethods(clazz, cf.Methods())

	return clazz
}

func (e *Class) IsPublic() bool {
	return e.accessFlags&ACC_PUBLIC == ACC_PUBLIC
}

func (e *Class) IsInterface() bool {
	return e.accessFlags&ACC_INTERFACE == ACC_INTERFACE
}

func (e *Class) IsAbstract() bool {
	return e.accessFlags&ACC_ABSTRACT == ACC_ABSTRACT
}

func (e *Class) isAccessibleTo(other *Class) bool {
	return e.IsPublic() || e.GetPackageName() == other.GetPackageName()
}

//region getters

// Name 类名
func (e *Class) Name() string {
	return e.name
}

func (e *Class) ConstantPool() *ConstantPool {
	return e.constantPool
}

func (e *Class) Fields() []*Field {
	return e.fields
}

func (e *Class) Methods() []*Method {
	return e.methods
}

func (e *Class) SuperClass() *Class {
	return e.superClass
}

func (e *Class) Loader() *ClassLoader {
	return e.loader
}

func (e *Class) JClass() *Object {
	return e.jClass
}

//endregion

//region 设置初始化

func (e *Class) StartInit() {
	e.initStarted = true
}

func (e *Class) InitStarted() bool {
	return e.initStarted
}

func (e *Class) IsSuper() bool {
	return e.accessFlags&ACC_SUPER == ACC_SUPER
}

func (e *Class) StaticVars() Slots {
	return e.staticVars
}

//endregion

func (e *Class) GetPackageName() string {
	if i := strings.LastIndex(e.name, "/"); i >= 0 {
		return e.name[:i]
	}
	return ""
}

func (e *Class) NewObject() *Object {
	return newObject(e)
}

//region 获取方法

// getStaticMethod 获取静态方法
func (e *Class) getStaticMethod(name, descriptor string) *Method {
	for _, m := range e.methods {
		if m.IsStatic() && m.name == name && m.descriptor == descriptor {
			return m
		}
	}
	return nil
}

func (e *Class) GetClientMethod() *Method {
	return e.getStaticMethod("<client>", "()V")
}

func (e *Class) GetMainMethod() *Method {
	return e.getStaticMethod("main", "([Ljava/lang/String;)V")
}

//endregion

func (e *Class) getField(name, descriptor string, isStatic bool) *Field {
	for c := e; e != nil; c = c.superClass {
		for _, field := range c.fields {
			if field.IsStatic() == isStatic && field.name == name && field.descriptor == descriptor {
				return field
			}
		}
	}
	return nil
}

func (e *Class) isJ1Object() bool {
	return e.name == "java/lang/Object"
}

func (e *Class) isJlCloneable() bool {
	return e.name == "java/lang/Cloneable"
}
func (e *Class) isJioSerializable() bool {
	return e.name == "java/io/Serializable"
}

func (e *Class) JavaName() string {
	return strings.Replace(e.name, "/", ".", -1)
}

func (e *Class) IsPrimitive() bool {
	_, ok := primitiveTypes[e.name]
	return ok
}

func (e *Class) ArrayClass() *Class {
	arrayClassName := getArrayClassName(e.name)
	return e.loader.LoadClass(arrayClassName)
}
